/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.component.spreadsheet.analyze;

import cn.ac.nercis.pes.common.utils.ConvertUtils;
import cn.ac.nercis.pes.common.utils.JsonUtils;
import cn.ac.nercis.pes.common.utils.PlaceholderUtils;
import cn.ac.nercis.pes.community.component.spreadsheet.PesSheetCellType;
import cn.ac.nercis.pes.community.component.spreadsheet.TableSheetView;
import cn.ac.nercis.pes.community.model.vo.evaluation.ResultCorrectionVO;
import cn.ac.nercis.pes.community.support.event.EventCallback;
import cn.ac.nercis.pes.community.utils.IconUtils;
import cn.ac.nercis.pes.community.model.vo.evaluation.EvaluationRequireVO;
import cn.ac.nercis.pes.community.support.event.EventCallback;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.spreadsheet.GridBase;
import org.controlsfx.control.spreadsheet.SpreadsheetCell;
import org.controlsfx.control.spreadsheet.SpreadsheetCellType;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 整体测评表控件
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public class EvalCorrectionSheetView extends TableSheetView<ResultCorrectionVO> {

    private final static Integer EMPTY_DATA_COUNT = 3;
    private final static List<String> COLUMNS = List.of("测评单元","测评对象","弥补前结论/分值","弥补后测评结果","弥补对象","弥补后分值","弥补原因","选择弥补对象");
    private final static Integer UNIT_COLUMN_INDEX = 0;
    private final static Integer NAME_COLUMN_INDEX = 1;
    private final static Integer BEFORE_RESULT_COLUMN_INDEX = 2;
    private final static Integer RESULT_COLUMN_INDEX = 3;
    private final static Integer ASSET_COLUMN_INDEX = 4;
    private final static Integer SCORE_COLUMN_INDEX = 5;
    private final static Integer DESC_COLUMN_INDEX = 6;
    private final static Integer OPTION_COLUMN_INDEX = 7;

    private EventCallback<ResultCorrectionVO> onOptionAction;

    public EvalCorrectionSheetView(){
        this.getStyleClass().add("pes-sheet-view");

        var grid = new GridBase(EMPTY_DATA_COUNT, COLUMNS.size());
        grid.getColumnHeaders().addAll(COLUMNS);
        grid.setRowHeightCallback(new GridBase.MapBasedRowHeightFactory(heardRowHeight()));
        setGrid(grid);
        getColumns().get(UNIT_COLUMN_INDEX).setPrefWidth(200);
        getColumns().get(NAME_COLUMN_INDEX).setPrefWidth(200);
        getColumns().get(BEFORE_RESULT_COLUMN_INDEX).setPrefWidth(120);
        getColumns().get(RESULT_COLUMN_INDEX).setPrefWidth(120);
        getColumns().get(ASSET_COLUMN_INDEX).setPrefWidth(200);
        getColumns().get(SCORE_COLUMN_INDEX).setPrefWidth(120);
        getColumns().get(DESC_COLUMN_INDEX).setPrefWidth(240);
        getColumns().get(OPTION_COLUMN_INDEX).setPrefWidth(120);
    }

    public void initialize(EventCallback<ResultCorrectionVO> onOptionAction){
        this.onOptionAction = onOptionAction;
        this.editModel.set(false);
    }

    public void loadContent(List<ResultCorrectionVO> data){
        this.editModel.set(false);
        this.sheetData = data;
        this.clearContent();
        for(var dataIndex=0;dataIndex<data.size();dataIndex++){
            this.loadContent(data.get(dataIndex),toSheetRowIndex(dataIndex));
        }
        setGrid(getGrid());
        this.editModel.set(true);
    }

    public void refreshOffsetAssetInfo(@NonNull ResultCorrectionVO correction){
        this.editModel.set(false);
        AtomicInteger dataIndex = new AtomicInteger(0);
        this.sheetData.forEach(item->{
            if(StringUtils.equals(item.getAssetName(),correction.getAssetName())){
                var rowData = this.getGrid().getRows().get(toSheetRowIndex(dataIndex.get()));
                refreshCellValue(rowData.get(RESULT_COLUMN_INDEX),correction.getResult());
                refreshCellValue(rowData.get(ASSET_COLUMN_INDEX),correction.getOffsetAssetName());
                refreshCellValue(rowData.get(SCORE_COLUMN_INDEX),correction.getScore());
            }
            dataIndex.getAndIncrement();
        });
        this.editModel.set(true);
    }

    private void refreshCellValue(SpreadsheetCell cell,String value){
        if(cell.isEditable()){
            cell.setItem(value);
        }else{
            cell.setEditable(true);
            cell.setItem(value);
            cell.setEditable(false);
        }
    }

    public List<ResultCorrectionVO> getModifyData(){
        List<ResultCorrectionVO> data = new ArrayList<>();
        for(var sheetRowIndex=0;sheetRowIndex<this.getGrid().getRows().size();sheetRowIndex++){
            if(this.isEmptyData(sheetRowIndex)){
                continue;
            }
            data.add(getModifyData(sheetRowIndex));
        }
        return data.stream()
                .filter(item-> Objects.nonNull(item) && item.isChange())
                .collect(Collectors.toList());
    }

    @Override
    public ContextMenu getSpreadsheetViewContextMenu() {
        return generateContextMenu(false);
    }

    private ResultCorrectionVO getModifyData(Integer sheetRowIndex){
        if(toDataIndex(sheetRowIndex)<0){
            return null;
        }
        var rowData = this.getGrid().getRows().get(sheetRowIndex);
        ResultCorrectionVO info;
        if(toDataIndex(sheetRowIndex)<this.sheetData.size()){
            info = this.sheetData.get(toDataIndex(sheetRowIndex));
        } else {
            return null;
        }
        info.setDesc(rowData.get(DESC_COLUMN_INDEX).getText());
        return info;
    }

    protected Map<Integer,Double> heardRowHeight(){
        Map<Integer, Double> rowHeight = new HashMap<>(1);
        IntStream.range(0,20).forEach(number->rowHeight.put(number,80.0));
        return rowHeight;
    }

    @Override
    protected void loadContent(ResultCorrectionVO info,Integer sheetRowIndex){
        final ObservableList<SpreadsheetCell> data = FXCollections.observableArrayList();
        AtomicInteger columnIndex = new AtomicInteger(0);
        data.add(getTextAreaCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getUnitTargetName(),false));
        data.add(getTextAreaCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getAssetName(),false));
        data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getBeforeResult(),false));
        data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getResult(),false));
        data.add(getTextAreaCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getOffsetAssetName(),false));
        data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getScore(),false));
        data.add(getTextAreaCell(sheetRowIndex,columnIndex.getAndIncrement(),descPreview(info.getDesc(),info),true));
        var optionCell = getOptionCell(sheetRowIndex,columnIndex.getAndIncrement());
        Button option = new Button();
        option.getStyleClass().add("sheet-view-option-button");
        option.setGraphic(IconUtils.getGlyph("evaluation-button-icon","sheet-view-option-icon"));
        String assetName = info.getAssetName();
        String unitTargetName = info.getUnitTargetName();
        option.setOnAction((event)->{
            var currentInfo = this.sheetData.stream()
                    .filter(s -> StringUtils.equals(s.getAssetName(), assetName)
                            && StringUtils.equals(s.getUnitTargetName(), unitTargetName))
                    .findFirst().orElse(null);
            if(Objects.nonNull(this.onOptionAction) && Objects.nonNull(currentInfo)){
                log.info(JsonUtils.toJson(currentInfo));
                this.onOptionAction.action(currentInfo);
            }
        });
        optionCell.setGraphic(option);
        data.add(optionCell);
        this.getGrid().getRows().add(data);
        info.resetChange();
    }

    private String descPreview(String templateContent,ResultCorrectionVO data){
        if(StringUtils.isBlank(templateContent)){
            return StringUtils.EMPTY;
        }
        return PlaceholderUtils.builder()
                .withAssetName(data.getOffsetAssetName())
                .withAssetScore(ConvertUtils.scoreToString(data.getOffsetScore(),""))
                .build()
                .renderContent(templateContent);
    }

    protected SpreadsheetCell getTextFieldCell(int row,int column,String value,boolean editable){
        var cell = SpreadsheetCellType.STRING
                .createCell(row,column,1,1,value);
        cell.setEditable(editable);
        cell.getStyleClass().add("data-cell");
        cell.textProperty().addListener(this::checkCellChanged);
        if(!editable){
            cell.getStyleClass().add("data-disable-cell");
        }
        return cell;
    }

    protected SpreadsheetCell getTextAreaCell(int row,int column,String value,boolean editable){
        var cell = PesSheetCellType.TEXT_AREA
                .createCell(row,column,1,1,value);
        cell.setEditable(editable);
        cell.setWrapText(true);
        cell.getStyleClass().add("data-cell");
        cell.textProperty().addListener(this::checkCellChanged);
        if(!editable){
            cell.getStyleClass().add("data-disable-cell");
        }
        return cell;
    }
    @Override
    protected void clearContent(){
        int endIndex = this.getGrid().getRows().size() -1;
        for(;endIndex>=0;endIndex--){
            this.getGrid().getRows().remove(endIndex);
        }
    }

    @Override
    protected boolean isEmptyData(Integer sheetRowIndex) {
        return toDataIndex(sheetRowIndex) < 0;
    }

    @Override
    protected boolean validate(ObservableList<SpreadsheetCell> rowData) {
        return true;
    }

    @Override
    protected void clearValidate(ObservableList<SpreadsheetCell> rowData) {
    }

    @Override
    protected List<String> getDataColumns() {
        return COLUMNS;
    }

    @Override
    protected ObservableList<SpreadsheetCell> generateEmptyDataRow(int dataIndex) {
        final ObservableList<SpreadsheetCell> data = FXCollections.observableArrayList();
        AtomicInteger columnIndex = new AtomicInteger(0);
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),"",false));
        return data;
    }

    @Override
    protected void deleteData() {
    }

}